// Copyright 2019 The rhyme_lph Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
// H [author] rhyme_lph
// Y [email]  rhymelph@gmail.com
// v [github] https://github.com/rhymelph

import 'dart:async';

import 'package:base_plugin/base_plugin.dart';
import 'package:dio/src/response.dart';
import 'package:finance_app/generated/l10n.dart';
import 'package:finance_app/src/common/config.dart';
import 'package:finance_app/src/entities/cpf_contribution_entity.dart';
import 'package:finance_app/src/entities/payroll_item_value.dart';
import 'package:finance_app/src/entities/payslips_entity.dart';
import 'package:finance_app/src/entities/payslips_item_entity.dart';
import 'package:finance_app/src/http_service/http_cpf_contribution_service.dart';
import 'package:finance_app/src/http_service/http_payslips_service.dart';
import 'package:finance_app/src/mixin/refresh_mixin.dart';
import 'package:finance_app/src/router_gen/router_gen_providers.dart';
import 'package:finance_app/src/utils/date_time_utils.dart';
import 'package:finance_app/src/utils/permission_utils.dart';
import 'package:finance_app/src/widgets/bottom_sheet/judge_bottom_sheet.dart';
import 'package:flutter/cupertino.dart';

import '../hr_and_payroll_provider.dart';

class PayrollProvider extends BaseProvider<PayslipsEntity>
    with RefreshMixin<PayslipsEntity, List<PayslipsItemEntity>> {
  List<PayslipsItemEntity> payslipsList = [];
  List<CpfContributionEntity> cpfsList = [];
  Map<String, PayrollItemValue> payslipsMap = {};

  @override
  Future get dataFuture => HttpPayslipsService.dashboardPayslips();

  String errorStatus = 'double_submission';
  ScrollController controller = ScrollController();

  @override
  void initState() {
    super.initState();
    controller.addListener(() {
      getParentProvider<HrAndPayrollProvider>().handleScrollUpdate(controller);
    });
  }

  @override
  FutureOr<PayslipsEntity> transformData(PayslipsEntity d, bool isCache) async {
    initPayslipsMap();
    await _loadPayslipsData();
    if (Config.hasPermission(PermissionUtils.hrPayroll)) await loadCPFData();
    return super.transformData(d, isCache);
  }

  void initPayslipsMap() {
    payslipsMap.clear();
    if (Config.hasPermission(PermissionUtils.hrPayroll)) {
      payslipsMap['open'] = PayrollItemValue()..data = [];
      payslipsMap['closed'] = PayrollItemValue()..data = [];
    } else {
      payslipsMap['This Month'] = PayrollItemValue()..data = [];
      payslipsMap['Past Months'] = PayrollItemValue()..data = [];
    }
  }

  Future<void> _loadPayslipsData() async {
    await post(refreshFuture, autoHandler: AutoHandler.Toast,
        onSuccess: (result) {
      payslipsList = getEntityFromResult<List<PayslipsItemEntity>>(result);
      formatMap(payslipsList);
    });
  }

  Future<void> loadCPFData() async {
    await post(HttpCpfContributionService.getCpfContributions(),
        autoHandler: AutoHandler.Toast, onSuccess: (result) {
      cpfsList = getEntityFromResult<List<CpfContributionEntity>>(result);
    });
  }

  void formatMap(List<PayslipsItemEntity> payslipsList) {
    if (Config.isStaff) {
      DateTime time = DateTime.now();
      for (final item in payslipsList) {
        final startTime =
            DateTimeUtils.toDateTime(item.startDate, 'yyyy-MM-dd');
        if (time.year == startTime.year && time.month == startTime.month) {
          if (payslipsMap['This Month'] == null) {
            payslipsMap['This Month'] = PayrollItemValue()..data = [item];
          } else {
            payslipsMap['This Month'].data.add(item);
          }
        } else {
          if (payslipsMap['Past Months'] == null) {
            payslipsMap['Past Months'] = PayrollItemValue()..data = [item];
          } else {
            payslipsMap['Past Months'].data.add(item);
          }
        }
      }
    } else {
      for (final item in payslipsList) {
        if (item.status == errorStatus) {
          if (payslipsMap['open'] == null) {
            payslipsMap['open'] = PayrollItemValue()..data = [item];
          } else {
            payslipsMap['open'].data.add(item);
          }
        } else {
          if (payslipsMap[item.status] == null) {
            payslipsMap[item.status] = PayrollItemValue()..data = [item];
          } else {
            payslipsMap[item.status].data.add(item);
          }
        }
      }
      if (payslipsMap['closed'] == null) {
        payslipsMap['closed'] = PayrollItemValue()..data = [];
      }
      if (payslipsMap['open'] == null) {
        payslipsMap['open'] = PayrollItemValue()..data = [];
      }
    }
  }

  String getGroupName(String key) {
    if (key == 'open') {
      return S.current.unpaid_payslips;
    } else if (key == 'closed') {
      return S.current.paid_payslips;
    } else {
      return key;
    }
  }

  void onNavPayslipDetail(PayslipsItemEntity e) async {
    obtainContext?.call((context) {
      FocusScope.of(context).requestFocus(FocusNode());
    });
    if (e.errorMessage != null || e.status == errorStatus) {
      obtainContext?.call((context) async {
        final result = await showDeletePayrollJudgeBottomSheet(context);
        if (result == true) {
          bool isSuccess = false;
          await post(HttpPayslipsService.payslipsDelete(e.id),
              autoHandler: AutoHandler.Toast,
              loadingTip: S.current.loading, onSuccess: (result) {
            isSuccess = true;
          });
          if (isSuccess) {
            payslipsList.remove(e);
            initPayslipsMap();
            formatMap(payslipsList);
          }
        }
      });
    } else {
      final result = await Routes.navigateTo(
          RouterGenProviders.payrollDetailPage,
          params: {
            'data': e,
          });
      if (result != null && result is Map && result['delete'] == true) {
        payslipsList.remove(e);
        initPayslipsMap();
        formatMap(payslipsList);
      }
    }
  }

  void onNavCPFDetail(CpfContributionEntity e) async {
    final result = await Routes.navigateTo(
        RouterGenProviders.recordCpfContributionPage,
        params: {
          'data': e,
        });
    if (result != null && result['delete'] == true) {
      loadCPFData();
    }
  }

  void updateCpfItem(CpfContributionEntity entity) {
    CpfContributionEntity data = cpfsList
        .firstWhere((element) => element.id == entity.id, orElse: () => null);
    if (data == null) {
      cpfsList.add(entity);
    } else {
      data.fromJson(entity.toJson());
    }
  }

  void updateCpfStatus(int id, String status) async {
    CpfContributionEntity type =
        cpfsList.firstWhere((element) => element.id == id, orElse: () => null);
    type?.status = status;
    update();
  }

  void updatePayslipItem(PayslipsItemEntity entity) {
    PayslipsItemEntity type = payslipsList
        .firstWhere((element) => element.id == entity.id, orElse: () => null);
    if (type == null) {
      payslipsList.add(entity);
    } else {
      type.fromJson(entity.toJson());
    }
    initPayslipsMap();
    formatMap(payslipsList);
  }

  void updatePayslipStatus(
      int id, String s, DateTime paidAt, String paymentTypeName) async {
    PayslipsItemEntity type = payslipsList
        .firstWhere((element) => element.id == id, orElse: () => null);
    type.status = s;
    type.paymentTypeName = paymentTypeName;
    type.paidAt = DateTimeUtils.formatDateTime(paidAt, 'yyyy-MM-dd HH:mm:ss');
    initPayslipsMap();
    formatMap(payslipsList);
    update();
    await loadDashboard();
    update();
  }

  void batchUpdatePayslipStatus(
      List<int> ids, String s, DateTime paidAt, String paymentTypeName) async {
    List<PayslipsItemEntity> types =
        payslipsList.where((element) => ids.contains(element.id)).toList();
    types.forEach((type) {
      type.status = s;
      type.paymentTypeName = paymentTypeName;
      type.paidAt = DateTimeUtils.formatDateTime(paidAt, 'yyyy-MM-dd HH:mm:ss');
    });
    initPayslipsMap();
    formatMap(payslipsList);
    update();
    await loadDashboard();
    update();
  }

  @override
  List<PayslipsItemEntity> get dataList => payslipsList;

  @override
  List<PayslipsItemEntity> getFromRequestResult(
      Response<dynamic> result, bool isRefresh) {
    List<PayslipsItemEntity> list =
        getEntityFromResult<List<PayslipsItemEntity>>(result);
    initPayslipsMap();
    formatMap(list);
    return list;
  }

  @override
  Future get refreshFuture => HttpPayslipsService.payslipsList();

  @override
  Future<void> onRefresh() async {
    await loadDashboard();
    await loadCPFData();
    return super.onRefresh();
  }

  Future<void> loadDashboard() async {
    await post(dataFuture, autoHandler: AutoHandler.Toast, onSuccess: (result) {
      data = getEntityFromResult<PayslipsEntity>(result);
    });
  }

  void batchDelete(List<int> ids) {
    payslipsList.removeWhere((element) => ids.contains(element.id));
    initPayslipsMap();
    formatMap(payslipsList);
    update();
  }
}
